#pragma once
#include "../PocelainMacro.h"
POCELAIN_NAMESPACE_BEGIN
template<class T,class ...Args>
T *CreateRef(Args ...args);

class POCELAIN_EXPORT Ref
{
public:
    Ref();
    virtual ~Ref();
    void release();
    void retain();
    int refCount()const{return mRefCount;};
    // Be friend for CreateRef template function
    template<class T,class ...Args>
    friend T *CreateRef(Args ...args);
private:
    Ref *autoRelease();
    int mRefCount;
};

template<class T>
class RefPtr
{
public:
    typedef RefPtr<T> type;
    explicit RefPtr(T *ptr = nullptr)
    {
        this->reset(ptr);
    }
    RefPtr(const type &other)
    {
        this->reset(other.mPtr);
    }
    RefPtr(type &&other)
    {
        this->reset(other.mPtr);
        other.reset(nullptr);
    }
    virtual ~RefPtr()
    {
        reset(nullptr);
    }
    RefPtr &operator=(const type &other)
    {
        this->reset(other.mPtr);
    }
    RefPtr &operator=(type &&other)
    {
        this->reset(other.mPtr);
        other.reset();
    }
    bool operator == (const type &other) const { return this->mPtr == other.mPtr; }
    bool operator != (const type &other) const { return this->mPtr != other.mPtr; }
    bool operator == (const T *ptr) const { return this->mPtr == ptr; }
    bool operator != (const T *ptr) const { return this->mPtr != ptr; }
    void reset(T *ptr)
    {
        if(ptr!=mPtr)
        {
            if(mPtr!=nullptr)
            {
                mPtr->release();
            }
            mPtr=ptr;
            if(mPtr!=nullptr)
            {
                mPtr->retain();
            }
        }
    }
    operator bool() const
    {
        return mPtr!=nullptr;
    }
    T &operator*() const
    {
        return *mPtr;
    }
    T *operator->() const
    {
        return mPtr;
    }
    T *get(){return mPtr;};
protected:
    T *mPtr = nullptr;
};

template<class T,class ...Args>
T *CreateRef(Args ...args)
{
    POCELAIN_STATIC_ASSERT(std::is_base_of<Ref,T>::value,"Error: Not inherit from Object.");
    auto ret = new T(args...);
    ret->autoRelease();
    return ret;
}
POCELAIN_NAMESPACE_END
